package io.gitee.zhangbinhub.acp.boot.exceptions

import com.fasterxml.jackson.databind.ObjectMapper
import io.gitee.zhangbinhub.acp.boot.log.LogAdapter
import io.gitee.zhangbinhub.acp.boot.tools.PackageTools
import io.gitee.zhangbinhub.acp.core.common.CommonTools
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.web.server.ServerWebExchange
import org.springframework.web.server.WebExceptionHandler
import reactor.core.publisher.Mono
import java.nio.charset.Charset

class ReactiveExceptionHandler(
    private val logAdapter: LogAdapter,
    private val acpWebExceptionHandler: List<AcpWebExceptionHandler>,
    private val objectMapper: ObjectMapper
) : WebExceptionHandler {
    fun handleGlobalError(ex: Throwable): ErrorResult =
        acpWebExceptionHandler.firstOrNull { handler -> handler.identify(ex) }?.handle(ex)
            ?: PackageTools.buildErrorResult(HttpStatus.INTERNAL_SERVER_ERROR, ex.message)

    override fun handle(exchange: ServerWebExchange, ex: Throwable): Mono<Void> = exchange.response.let { response ->
        if (response.isCommitted) {
            Mono.error(ex)
        } else {
            logAdapter.error(ex.message, ex)
            handleGlobalError(ex).let { errorResult ->
                response.headers.contentType = MediaType.APPLICATION_JSON
                response.statusCode = errorResult.httpStatus
                val data = try {
                    objectMapper.writeValueAsString(errorResult.errorVo)
                } catch (e: Throwable) {
                    logAdapter.error(e.message, e)
                    "{\"error\":\"${ex.message}\"}"
                }
                logAdapter.error("response error info [${errorResult.httpStatus}] : $data")
                response.writeWith(
                    Mono.just(
                        response.bufferFactory()
                            .wrap(data.toByteArray(Charset.forName(CommonTools.getDefaultCharsetName())))
                    )
                )
            }
        }
    }
}
